package org.moonlight.jvm.instructions.extended;

import org.moonlight.jvm.instructions.base.BytecodeReader;
import org.moonlight.jvm.instructions.base.Instruction;
import org.moonlight.jvm.instructions.loads.aload.ALoad;
import org.moonlight.jvm.instructions.loads.dload.DLoad;
import org.moonlight.jvm.instructions.loads.fload.FLoad;
import org.moonlight.jvm.instructions.loads.iload.ILoad;
import org.moonlight.jvm.instructions.loads.lload.LLoad;
import org.moonlight.jvm.instructions.math.arithmetic.IInc;
import org.moonlight.jvm.instructions.stores.astore.AStore;
import org.moonlight.jvm.instructions.stores.dstore.DStore;
import org.moonlight.jvm.instructions.stores.fstore.FStore;
import org.moonlight.jvm.instructions.stores.istore.IStore;
import org.moonlight.jvm.instructions.stores.lstore.LStore;
import org.moonlight.jvm.rtda.exclusive.Frame;

/**
 * WIDE: 加载类指令、存储类指令、ret指令 和 iinc指令 需要按索引访问局部变量表，索引以 uint8 的形式存在字节码中。
 *       对于大部分方法来说，局部变量表大小都不会超过256，所以用一字节来表示索引就够了。
 *       但是特殊情况下可能会有方法的局部变量表超过这个限制，所以Java虚拟机规范定义了wide指令来扩展前述指令。
 *
 * wide指令改变其他指令的行为，modifiedInstruction字段存放被改变的指令。wide指令需要自己解码出modifiedInstruction,
 * 但是这个改变是指 增加了索引宽度，它并没有改变子指令的操作
 *
 * @author Moonlight
 * @createTime 2023/8/23 9:43
 **/
public class Wide implements Instruction {

    /** 被改变的指令 **/
    private Instruction modifiedInstruction;

    @Override
    public void fetchOperands(BytecodeReader reader) {
        int opCode = reader.readInt8();
        switch (opCode) {
            case 0x15:
                ILoad iLoad = new ILoad();
                iLoad.setIdx(reader.readInt16());
                this.modifiedInstruction = iLoad;
                break;
            case 0x16:
                LLoad lLoad = new LLoad();
                lLoad.setIdx(reader.readInt16());
                this.modifiedInstruction = lLoad;
                break;
            case 0x17:
                FLoad fLoad = new FLoad();
                fLoad.setIdx(reader.readInt16());
                this.modifiedInstruction = fLoad;
                break;
            case 0x18:
                DLoad dLoad = new DLoad();
                dLoad.setIdx(reader.readInt16());
                this.modifiedInstruction = dLoad;
                break;
            case 0x19:
                ALoad aLoad = new ALoad();
                aLoad.setIdx(reader.readInt16());
                this.modifiedInstruction = aLoad;
                break;
            case 0x36:
                IStore iStore = new IStore();
                iStore.setIdx(reader.readInt16());
                this.modifiedInstruction = iStore;
                break;
            case 0x37:
                LStore lStore = new LStore();
                lStore.setIdx(reader.readInt16());
                this.modifiedInstruction = lStore;
                break;
            case 0x38:
                FStore fStore = new FStore();
                fStore.setIdx(reader.readInt16());
                this.modifiedInstruction = fStore;
                break;
            case 0x39:
                DStore dStore = new DStore();
                dStore.setIdx(reader.readInt16());
                this.modifiedInstruction = dStore;
                break;
            case 0x3a:
                AStore aStore = new AStore();
                aStore.setIdx(reader.readInt16());
                this.modifiedInstruction = aStore;
                break;
            case 0x84:
                IInc iInc = new IInc();
                iInc.setIdx(reader.readInt16());
                iInc.setConstVal(reader.readInt16());
                this.modifiedInstruction = iInc;
                break;
            case 0xa9:
                // ret
                throw new ClassFormatError("Unsupported opcode: 0xa9");
            default:
                throw new ClassFormatError("unknow opCode[" + opCode + "] in WIDE Instruction");
        }
    }

    @Override
    public void execute(Frame frame) {
        this.modifiedInstruction.execute(frame);
    }
}
